package api.loader;

import api.VoucherFileInfo;
import api.tools.FileTool;
import com.pansoft.xbrl.xbrljson.util.PropUtil;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class OFDLoader {

    private String attPath = "";
    private List<String> attachments = null;

    private ZipFile loadOFD(String filePath) throws Exception {
        ZipFile file = new ZipFile(filePath);
        ZipArchiveEntry entry = file.getEntry("OFD.xml");
        if (entry == null) {
            file.close();
            return null;
        }
        Document d = entryToDocument(file, entry);
        NodeList list = d.getElementsByTagName("ofd:DocBody");
        for (int i = 0; i < list.getLength(); i++) {
            Node node = list.item(i);
            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            parserDocumentBody(node, file);
        }
        return file;
    }

    private void parserDocumentBody(Node nd, ZipFile file) throws Exception {
        String docPath = null;
        NodeList list = nd.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node node = list.item(i);
            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            if ("ofd:DocRoot".equalsIgnoreCase(node.getNodeName())) {
                docPath = node.getTextContent().trim();
            }
        }
        if (docPath == null || docPath.length() < 1) {
            return;
        }
        if (docPath.startsWith("/") || docPath.startsWith("\\")) {
            docPath = docPath.substring(1);
        }
        ZipArchiveEntry entry = file.getEntry(docPath);
        Document d = entryToDocument(file, entry);
        parserDocument(d.getDocumentElement(), file);
    }

    private void parserDocument(Node node, ZipFile file) throws Exception {
        if (node.getNodeType() != Node.ELEMENT_NODE) {
            return;
        }
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node nd = list.item(i);
            if (nd.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            if ("ofd:Attachments".equalsIgnoreCase(nd.getNodeName())) {
                attPath = nd.getTextContent().trim();
                if (!attPath.startsWith("/") && !attPath.startsWith("\\")) {
                    attPath = "Doc_0/" + attPath;
                }
                break;
            }
        }
        ZipArchiveEntry entry;
        if (attPath.length() > 0) {
            entry = file.getEntry(attPath);
            Document d = entryToDocument(file, entry);
            parseAttachment(d.getDocumentElement());
        }
    }

    // 将zip文件中的xml entry解析成document
    private Document entryToDocument(ZipFile file, ZipArchiveEntry entry) throws Exception {
        InputStream is = null;
        try {
            is = file.getInputStream(entry);
            DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = fac.newDocumentBuilder();
            return builder.parse(is);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (null != is) {
                is.close();
            }
        }

    }

    private void parseAttachment(Node node) {
        if (node.getNodeType() != Node.ELEMENT_NODE)
            return;
        NodeList nlist = node.getChildNodes();
        for (int j = 0; j < nlist.getLength(); j++) {
            Node nd = nlist.item(j);
            if (nd.getNodeType() == Node.ELEMENT_NODE) {
                String path = nd.getTextContent().trim();
                if (!path.startsWith("/") && !path.startsWith("\\"))
                    path = this.attPath.substring(0,
                            this.attPath.lastIndexOf("/") + 1)
                            + path;
                if (attachments == null)
                    attachments = new ArrayList<String>();
                if (path.startsWith("/") || path.startsWith("\\")) {
                    path = path.substring(1);
                }
                attachments.add(path);
            }
        }
    }

    public VoucherFileInfo extractAttach(String ofdFilePath, String outputFile)
            throws Exception {

        ZipFile file = null;
        VoucherFileInfo voucherFileInfo = new VoucherFileInfo();
        try {
            file = loadOFD(ofdFilePath);

            if (this.attachments == null || file == null) {
                return null;
            }

            String fileName;
            String outFilePath = outputFile;
            String voucherTypeValue = PropUtil.getPropValue("VoucherType", "VoucherType");
            String[] voucherTypeArray = voucherTypeValue.split(",");

            for (int i = 0; i < this.attachments.size(); i++) {
                String fullPath = this.attachments.get(i);
                fileName = fullPath.substring(fullPath.lastIndexOf("/") + 1);
                // 如未指定输出文件，则存放到临时目录下
                if (outFilePath == null || "".equals(outFilePath)) {
                    outFilePath = System.getProperty("java.io.tmpdir") + File.separator + fileName;
                }

                for (int j = 0; j < voucherTypeArray.length; j++) {
                    if (fileName.startsWith(voucherTypeArray[j])) {
                        extract(file, fullPath, outFilePath);
                        voucherFileInfo.setVoucherType(voucherTypeArray[j]);
                        voucherFileInfo.setXbrlFilePath(outFilePath);
                        return voucherFileInfo;
                    }
                }

                if (this.attachments.size() - 1 == i) {
                    voucherFileInfo.setXbrlFilePath(outFilePath);
                    extract(file, fullPath, outFilePath);
                }
            }
            return voucherFileInfo;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (null != file) {
                file.close();
            }
        }
    }

    private void extract(ZipFile file, String fullPath, String outFilePath) throws IOException {
        ZipArchiveEntry entry;
        entry = file.getEntry(fullPath);
        InputStream is = null;
        try {
            is = file.getInputStream(entry);
            FileTool.saveTo(is, outFilePath);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (null != is) {
                is.close();
            }
        }
    }
}